package org.cfx.gift.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import io.seata.spring.annotation.GlobalTransactional;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.cfx.gift.client.AuthClient;
import org.cfx.gift.client.UserClient;
import org.cfx.gift.domain.*;
import org.cfx.gift.dto.TenantDto;
import org.cfx.gift.dto.TenantEmailDto;
import org.cfx.gift.exception.BusinessException;
import org.cfx.gift.exception.azzert.RonghuaAssert;
import org.cfx.gift.exception.code.ResponseCode;
import org.cfx.gift.mapper.EmployeeMapper;
import org.cfx.gift.mapper.TenantMapper;
import org.cfx.gift.mapper.TenantMealMapper;
import org.cfx.gift.service.ITenantService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import org.cfx.gift.util.AjaxResult;
import org.cfx.gift.util.BaiduAuditUtils;
import org.cfx.gift.util.MD5Utils;
import org.cfx.gift.util.MailUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author cfx
 * @since 2023-11-01
 */
@Service
public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> implements ITenantService {

    @Autowired
    private TenantMapper tenantMapper;

    @Autowired
    private TenantMealMapper tenantMealMapper;

    @Qualifier("org.cfx.gift.client.AuthClient")
    @Autowired
    private AuthClient authClient;

    @Autowired
    private EmployeeMapper employeeMapper;

    @Qualifier("org.cfx.gift.client.UserClient")
    @Autowired
    private UserClient userClient;

    private final String receiver = "1905333495@qq.com";

    @Value("${update.url}")
    private String updateUrl;

    @Value("${activation.url}")
    private String activationUrl;

    @Autowired
    private MailUtil mailUtil;

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void tenantRegister(TenantDto dto) {
        System.out.println(dto);
        //---------进行百度ai审核文字----------
        Integer integer = employeeMapper.selectCount(new EntityWrapper<Employee>().eq("username", dto.getAdmin().getUsername()));
        checkTenantName(dto);
        //新建租户对象
        Tenant tenant = new Tenant();
        tenant.setCompanyName(dto.getCompanyName());
        tenant.setCompanyNum(dto.getCompanyNum());
        tenant.setRegisterTime(new Date());
        tenant.setAddress(dto.getAddress());
        tenant.setLogo(dto.getLogo());
        tenant.setState(1);
        //新建租户套餐对象
        TenantMeal tenantMeal = new TenantMeal();
        tenantMeal.setMealId(dto.getMealId());
        tenantMeal.setState(0);
        Date date = new Date();
        date.setTime(date.getTime() + 60 * 3000);
        tenantMeal.setExpireDate(new Date());
        //新建loginUser对象
        LoginUser loginUser = new LoginUser();
        String salt = UUID.randomUUID().toString();
        //加盐值
        String byMd5 = MD5Utils.encrypByMd5("cfx" + dto.getAdmin().getPassword() + salt + "2000");
        loginUser.setPhone(dto.getCompanyNum());
        loginUser.setType(0);
        loginUser.setUsername(dto.getAdmin().getUsername());
        loginUser.setEmail(dto.getAdmin().getEmail());
        loginUser.setSalt(salt);
        loginUser.setPassword(byMd5);
        if (dto.getAdmin().getLoginId() != null) loginUser.setId(dto.getAdmin().getLoginId());
        //远程调用 授权中心 保存登录信息
        AjaxResult ajaxResult = authClient.addOrUpdate(loginUser);
        // 如果调授权服务异常，就报错
        if (!ajaxResult.isSuccess()) {
            throw new BusinessException(ajaxResult.getMessage());
        }
        //新建管理员到对象
        Employee employee = new Employee();
        employee.setUsername(dto.getAdmin().getUsername());
        employee.setPassword(dto.getAdmin().getPassword());
        employee.setSalt(salt);
        employee.setEmail(dto.getAdmin().getEmail());
        //因为是远程调用，所有要从ajaxResult里面获取resultObj里面的数据
        Long resultObjId = Long.parseLong(ajaxResult.getResultObj().toString());
        employee.setLoginId(resultObjId);
        employee.setInputTime(new Date());
        employee.setType(5);
        employee.setTel(dto.getCompanyNum());
        employee.setState(1);
        //新建user对象
        User user = new User();
        //新建userInfo对象
        UserInfo userInfo = new UserInfo();
        //新增租户操作
        if (dto.getId() == null) {
            if (integer > 0) throw  new BusinessException("管理员账号已存在");
            checkName(dto);
            //1 保存租户
            tenantMapper.insert(tenant);
            //2 保存租户套餐
            tenantMeal.setTenantId(tenant.getId());
            tenantMealMapper.insert(tenantMeal);
            //3 保存租户管理员
            employee.setTenantId(tenant.getId());
            employeeMapper.insert(employee);
            //4 设置租户管理员id
            tenant.setAdminId(employee.getId());
            //把租户管理员id保存
            tenantMapper.updateById(tenant);
        }else {
            Tenant tenant1 = tenantMapper.selectById(dto.getId());
            if (!Objects.equals(tenant.getCompanyName(), tenant1.getCompanyName())){
                checkName(dto);
            }
            tenant.setId(dto.getId());
            //根据租户id 更新租户信息
            tenantMapper.updateById(tenant);
            //先删除租户套餐，再新增
            TenantMeal tenantMeal1 = tenantMealMapper.queryById(dto.getId());
            tenantMealMapper.deleteByTenantId(tenantMeal1.getTenantId());
            //保存租户套餐信息
            tenantMeal.setTenantId(dto.getId());
            tenantMealMapper.insert(tenantMeal);
            //通过查租户表，获取管理员id
            Tenant selectById = tenantMapper.selectById(dto.getId());
            employee.setId(selectById.getAdminId());
            employeeMapper.updateById(employee);
            //查询对应的loginUserId和userId信息
            Employee selectByAdminId = employeeMapper.selectById(selectById.getAdminId());
            AjaxResult byLoginId = userClient.getByLoginId(dto.getAdmin().getLoginId());
            //设置要更新的userId
            loginUser.setId(selectByAdminId.getId());
            // 如果调授权服务异常，就报错
            if (!ajaxResult.isSuccess()) {
                throw new BusinessException(ajaxResult.getMessage());
            }
            employee.setTenantId(dto.getId());
            //设置要更新的userId
            user.setId(Long.parseLong(byLoginId.getResultObj().toString()));
        }
        user.setLoginId(resultObjId);
        user.setSecLevel(0);
        user.setPhone(dto.getCompanyNum());
        user.setPassword(byMd5);
        user.setSalt(salt);
        user.setBitState(1L);
        user.setEmail(dto.getAdmin().getEmail());
        user.setCreateTime(System.currentTimeMillis());
        //保存user信息
        AjaxResult resultUserId = userClient.addOrUpdate(user);
        userInfo.setRegTime(System.currentTimeMillis());
        userInfo.setCreateTime(System.currentTimeMillis());
        userInfo.setLevel(0);
        userInfo.setGrowScore(0);
        //关联user id
        userInfo.setUserId(Long.parseLong(resultUserId.getResultObj().toString()));
        //保存user详情
        userClient.addOrUpdate(userInfo);
        //所有操作都没有问题，则可发送邮件通知租户
//        sendPassEmail(tenant, employee, dto);
        sendEmail(tenant, employee);
}

    /**
     * 判断租户昵称是否存在
     * @param dto 参数
     */
    public void checkName(TenantDto dto){
        Integer integer = tenantMapper.selectCount(new EntityWrapper<Tenant>().eq("company_name", dto.getCompanyName()));
        if (integer > 0) {
            throw new BusinessException("租户名称已存在！");
        }
    }

    /**
     * 判断租户名称是否违规
     *
     * @param dto 前端传的参数
     */
    public void checkTenantName(TenantDto dto) {
        Map<String, Object> shopMap = BaiduAuditUtils.textCensor(dto.getCompanyName());
        Boolean shopBoo = (Boolean) shopMap.get("success");
        if (!shopBoo) {
            throw new BusinessException("店铺名字中存在非法的字符串");
        }
        Map<String, Object> adminMap = BaiduAuditUtils.textCensor(dto.getAdmin().getUsername());
        Boolean adminBoo = (Boolean) adminMap.get("success");
        if (!adminBoo) {
            throw new BusinessException("管理员账号中存在非法的字符串");
        }
    }

    /**
     * 审核通过，发送邮件的具体实现类
     *
     * @param tenant   租户
     * @param employee 租户管理员
     * @param dto      租户信息在邮件中展示
     */
    public void sendPassEmail(Tenant tenant, Employee employee, TenantDto dto) {
        //发送邮件
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        String subject = "店铺审核通过";
        //随机生成6位 字母数字组合
        String code = RandomStringUtils.randomAlphanumeric(6);
        //以店铺id作为加密头
        String code2 = MD5Utils.code(tenant.getId().toString());
        //key为随机字符，value为租户信息，有效期5分钟。
        redisTemplate.opsForValue().set(code2 + "-" + code, employee, Duration.ofDays(1));
        String email = employee.getEmail();
        //把激活地址放到邮件里
        String url = activationUrl;
        String username = employee.getUsername();
        String emailTemplate = "passTemplate";
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("email", email);
        dataMap.put("code", code2 + "-" + code);
        dataMap.put("name", tenant.getCompanyName());
        dataMap.put("url", url);
        dataMap.put("username", username);
        dataMap.put("createTime", sdf.format(new Date()));
        try {
            //更新店铺状态（审核通过,待激活）
            tenant.setState(1);
            tenantMapper.updateById(tenant);
            //发送邮件
            mailUtil.sendTemplateMail(employee.getEmail(), subject, emailTemplate, dataMap);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(ResponseCode.RESPONSE_CODE_500);
        }
    }

    /**
     * 发送邮件的具体实现类
     *
     * @param tenant   租户
     * @param employee 租户管理员
     */
    public void sendNotPassEmail(Tenant tenant, Employee employee,TenantEmailDto shopEmailDto) {
        if (StringUtils.isBlank(employee.getEmail())) throw new BusinessException("管理员邮箱为空");
        //准备 发送邮件
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        String subject = "很抱歉，店铺审核未能通过";
        String email = employee.getEmail();
        String msg = shopEmailDto.getType();
        //店铺id使用uuid加密，并保存到redis
        long id = tenant.getId();
        String s = RandomStringUtils.randomAlphanumeric(10);
        redisTemplate.opsForValue().set(s, id, Duration.ofMinutes(5));
        // 租户重新入驻的链接
        String url = updateUrl + s;
        String reason = shopEmailDto.getReason();
        String emailTemplate = "notPassTemplate";
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("email", email);
        dataMap.put("msg", msg);
        dataMap.put("name", tenant.getCompanyName());
        dataMap.put("url", url);
        dataMap.put("reason", reason);
        dataMap.put("createTime", sdf.format(new Date()));
        try {
            //更新店铺状态 （审核失败）
            tenant.setState(2);
            tenantMapper.updateById(tenant);
            //更新管理员状态 (锁定)
            employee.setState(1);
            employeeMapper.updateById(employee);
            //调用发送邮件工具
            mailUtil.sendTemplateMail(employee.getEmail(), subject, emailTemplate, dataMap);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(ResponseCode.RESPONSE_CODE_500);
        }
    }

    public void sendEmail(Tenant tenant,Employee employee) {
        //发送邮件
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒");
        String subject = "店铺提交审核成功";
        String emailTemplate = "saveTemplate";
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("name", tenant.getCompanyName());
        dataMap.put("createTime", sdf.format(new Date()));
        try {
            //更新店铺状态（审核通过,待激活）
            tenant.setState(0);
            tenantMapper.updateById(tenant);
            //发送邮件
            mailUtil.sendTemplateMail(employee.getEmail(), subject, emailTemplate, dataMap);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BusinessException(ResponseCode.RESPONSE_CODE_500);
        }
    }

    /**
     * 根据redis里的uuid查租户
     *
     * @param uuid key 为uuid value为租户id
     * @return 做回显
     */
    @Override
    public TenantDto queryByUUId(String uuid) {
        // 1 从redis里拿
        Object o = redisTemplate.opsForValue().get(uuid);
        //2 如果不存在就报错
        RonghuaAssert.isNotNull(o, "链接已失效");
        // 3 否则就根据租户id查询租户、管理员等信息，并把信息传给去前端。
        Tenant tenant = tenantMapper.selectById(Long.valueOf(o.toString()));
        Employee employee = employeeMapper.selectById(tenant.getAdminId());
        TenantMeal tenantId = tenantMealMapper.queryById(tenant.getId());
        TenantDto tenantDto = new TenantDto();
        tenantDto.setId(tenantId.getTenantId());
        tenantDto.setAddress(tenant.getAddress());
        tenantDto.setLogo(tenant.getLogo());
        tenantDto.setCompanyName(tenant.getCompanyName());
        tenantDto.setCompanyNum(tenant.getCompanyNum());
        tenantDto.setMealId(tenantId.getMealId());
        tenantDto.setAdmin(employee);
        // 做回显
        return tenantDto;
    }

    /**
     * 判断激活码是否存在
     *
     * @param number 用户输入的激活码
     */
    @Override
    public void check(String number) {
        //1 从redistribution里面拿
        Object o = redisTemplate.opsForValue().get(number);
        // 2 不存在就报错给前端
        if (o == null) {
            throw new BusinessException("激活码已过期，请联系管理员！");
        }
        // 3 否则就把租户管理员的账号设为 正常状态
//        Map map = BeanUtil.toBean(o, Map.class);
//        map.get("");
        Employee employee = BeanUtil.toBean(o, Employee.class);
        System.out.println(employee);
        employee.setState(0);
        employeeMapper.updateById(employee);
        redisTemplate.delete(number);
        Tenant tenant = tenantMapper.selectById(employee.getTenantId());
        System.out.println(tenant);
        tenant.setState(4);
        tenantMapper.updateById(tenant);
//        System.out.println(o.toString());
    }

    /**
     * 在租户展示页面，进行人工审核
     *
     * @param shopEmailDto 不通过的原因和类型
     * @throws Exception 邮件异常
     */
    @Override
    public void sendNotPassEmail(TenantEmailDto shopEmailDto) throws Exception {
        //1 查询租户
        Tenant tenant = tenantMapper.selectById(shopEmailDto.getId());
        // 2 根据租户的admin_id 查询对应的管理员
        Employee employee = employeeMapper.selectById(tenant.getAdminId());
//        System.out.println(employee);
        //调用发邮件
        sendNotPassEmail(tenant, employee,shopEmailDto);
    }

    /**
     * 在租户展示页面，进行人工审核 审核通过再把管理员、租户等数据存redis
     *
     * @param shopEmailDto 只需要接收租户id
     * @throws Exception 邮件异常
     */
    @Override
    public void sendPassEmail(TenantEmailDto shopEmailDto) throws Exception {
        //1 查询租户
        Tenant tenant = tenantMapper.selectById(shopEmailDto.getId());
        // 2 根据租户的admin_id 查询对应的管理员
        Employee employee = employeeMapper.selectById(tenant.getAdminId());
        System.out.println(employee);
        // 3 查询套餐
        TenantMeal tenantId = tenantMealMapper.queryById(tenant.getId());
        TenantDto tenantDto = new TenantDto();
        tenantDto.setAddress(tenant.getAddress());
        tenantDto.setLogo(tenant.getLogo());
        tenantDto.setCompanyName(tenant.getCompanyName());
        tenantDto.setCompanyNum(tenant.getCompanyNum());
        tenantDto.setMealId(tenantId.getMealId());
        tenantDto.setAdmin(employee);
        //调用发邮件
        sendPassEmail(tenant, employee, tenantDto);
    }
}
